﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(InputEventManager))]
public class InputModule : MonoBehaviour {

	class ButtonState {
		
		public InputEventData.FramePressState buttonState;

		public InputEventData buttonData;

		public bool PressedThisFrame()
		{
			return this.buttonState == InputEventData.FramePressState.Pressed || this.buttonState == InputEventData.FramePressState.PressedAndReleased;
		}

		public bool ReleasedThisFrame()
		{
			return this.buttonState == InputEventData.FramePressState.Released || this.buttonState == InputEventData.FramePressState.PressedAndReleased;
		}
	}

	class MouseState {
		private ButtonState buttonState = new ButtonState();

		public ButtonState GetButtonState() {
			return buttonState;
		}

		public void SetButtonState(InputEventData.FramePressState stateForMouseBtn, InputEventData data) {
			buttonState.buttonState = stateForMouseBtn;
			buttonState.buttonData = data;
		}
	}

	private InputEventManager eventManager;

	private InputBase m_DefaultInput;

	protected Dictionary<int, InputEventData> m_PointerData = new Dictionary<int, InputEventData>();

	protected List<stRaycastResult> m_RaycastResultCache = new List<stRaycastResult>();

	private readonly MouseState m_MouseState = new MouseState();

	private GameObject m_CurrentFocusedGameObject;

	public InputBase input
	{
		get
		{
			InputBase result;
			if (this.m_DefaultInput == null)
			{
				this.m_DefaultInput = base.GetComponent<InputBase>();
				if (this.m_DefaultInput == null)
				{
					this.m_DefaultInput = base.gameObject.AddComponent<InputBase>();
				}
			}
			result = this.m_DefaultInput;
			return result;
		}
	}

	protected bool GetPointerData(int id, out InputEventData data, bool create)
	{
		bool result;
		if (!this.m_PointerData.TryGetValue(id, out data) && create)
		{
			data = new InputEventData(this.eventManager)
			{
				PointerId = id
			};
			this.m_PointerData.Add(id, data);
			result = true;
		}
		else
		{
			result = false;
		}
		return result;
	}

	void Start() {
		this.eventManager = GetComponent<InputEventManager> ();
	}

	public void Process() {
		if (this.eventManager.isFocused) {
			if (!this.ProcessTouchEvents () && input.mousePresent) {
				this.ProcessMouseEvent ();
			}
		}
	}

	private bool ProcessTouchEvents() {
		for (int i = 0; i < this.input.touchCount; i++)
		{
			Touch touch = this.input.GetTouch(i);
			if (touch.type != TouchType.Indirect)
			{
				bool pressed;
				bool released;
				InputEventData touchPointerEventData = this.GetTouchPointerEventData(touch, out pressed, out released);
				this.ProcessTouchPress(touchPointerEventData, pressed, released);
				if (!released)
				{
					this.ProcessMove(touchPointerEventData);
					this.ProcessDrag(touchPointerEventData);
				}
				else
				{
					this.RemovePointerData(touchPointerEventData);
				}
			}
		}
		return this.input.touchCount > 0;
	}

	#region touch
	protected InputEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released)
	{
		InputEventData pointerEventData;
		bool pointerData = this.GetPointerData(input.fingerId, out pointerEventData, true);
		pointerEventData.Reset();
		pressed = (pointerData || input.phase == TouchPhase.Began);
		released = (input.phase == TouchPhase.Canceled || input.phase == TouchPhase.Ended);
		if (pointerData)
		{
			pointerEventData.Position = input.position;
		}
		if (pressed)
		{
			pointerEventData.Delta = Vector2.zero;
		}
		else
		{
			pointerEventData.Delta = input.position - pointerEventData.Position;
		}
		pointerEventData.Position = input.position;
		this.eventManager.RaycastAll(pointerEventData, this.m_RaycastResultCache);
		stRaycastResult pointerCurrentRaycast = FindFirstRaycast(this.m_RaycastResultCache);
		pointerEventData.CurrentRaycast = pointerCurrentRaycast;
		this.m_RaycastResultCache.Clear();
		return pointerEventData;
	}

	protected void ProcessTouchPress(InputEventData pointerEvent, bool pressed, bool released)
	{
		GameObject gameObject = pointerEvent.CurrentRaycast.gameObject;
		if (pressed)
		{
			pointerEvent.eligibleForClick = true;
			pointerEvent.Delta = Vector2.zero;
			pointerEvent.dragging = false;
			pointerEvent.useDragThreshold = true;
			pointerEvent.pressPosition = pointerEvent.Position;
			pointerEvent.PressRaycast = pointerEvent.CurrentRaycast;
			if (pointerEvent.pointerEnter != gameObject)
			{
				this.HandlePointerExitAndEnter(pointerEvent, gameObject);
				pointerEvent.pointerEnter = gameObject;
			}
			GameObject gameObject2 = InputExecuteEvents.ExecuteHierarchy<IInputDownHandler>(gameObject, pointerEvent, InputExecuteEvents.InputDownHandler);
			if (gameObject2 == null)
			{
				gameObject2 = InputExecuteEvents.GetEventHandler<IInputClickHandler>(gameObject);
			}
			float unscaledTime = Time.unscaledTime;
			if (gameObject2 == pointerEvent.lastPress)
			{
				float num = unscaledTime - pointerEvent.clickTime;
				if (num < 0.3f)
				{
					pointerEvent.clickCount++;
				}
				else
				{
					pointerEvent.clickCount = 1;
				}
				pointerEvent.clickTime = unscaledTime;
			}
			else
			{
				pointerEvent.clickCount = 1;
			}
			pointerEvent.pointerPress = gameObject2;
			pointerEvent.rawPointerPress = gameObject;
			pointerEvent.clickTime = unscaledTime;
			pointerEvent.pointerDrag = InputExecuteEvents.GetEventHandler<IInputDragHandler>(gameObject);
		}
		if (released)
		{
			InputExecuteEvents.Execute<IInputUpHandler>(pointerEvent.pointerPress, pointerEvent, InputExecuteEvents.InputUpHandler);
			GameObject eventHandler = InputExecuteEvents.GetEventHandler<IInputClickHandler>(gameObject);
			if (pointerEvent.pointerPress == eventHandler && pointerEvent.eligibleForClick)
			{
				InputExecuteEvents.Execute<IInputClickHandler>(pointerEvent.pointerPress, pointerEvent, InputExecuteEvents.InputClickHandler);
			}
			pointerEvent.eligibleForClick = false;
			pointerEvent.pointerPress = null;
			pointerEvent.rawPointerPress = null;
			if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
			{
				InputExecuteEvents.Execute<IInputEndDragHandler>(pointerEvent.pointerDrag, pointerEvent, InputExecuteEvents.InputEndDragHandler);
			}
			pointerEvent.dragging = false;
			pointerEvent.pointerDrag = null;
			InputExecuteEvents.ExecuteHierarchy<IInputExitHandler>(pointerEvent.pointerEnter, pointerEvent, InputExecuteEvents.InputExitHandler);
			pointerEvent.pointerEnter = null;
		}
	}
	#endregion

	#region mouse
	private void ProcessMouseEvent() {
		MouseState mouseState = GetMouseState ();
		ButtonState buttonState = mouseState.GetButtonState ();
		this.m_CurrentFocusedGameObject = buttonState.buttonData.CurrentRaycast.gameObject;
//		if (buttonState.PressedThisFrame()) {
//			Debug.LogError ("PressedThisFrame");
//		}
//		if (buttonState.ReleasedThisFrame()) {
//			Debug.LogError ("ReleasedThisFrame");
//		}
//		if (this.m_CurrentFocusedGameObject)
//			Debug.LogError (buttonState.buttonData);
		this.ProcessMousePress (buttonState);
		this.ProcessMove (buttonState.buttonData);
		this.ProcessDrag (buttonState.buttonData);
	}

	private InputEventData.FramePressState StateForMouseButton(int buttonID = 0) {
		bool mouseButtonDown = input.GetMouseButtonDown(buttonID);
		bool mouseButtonUp = input.GetMouseButtonUp(buttonID);
		InputEventData.FramePressState result;
		if (mouseButtonDown && mouseButtonUp)
		{
			result = InputEventData.FramePressState.PressedAndReleased;
		}
		else if (mouseButtonDown)
		{
			result = InputEventData.FramePressState.Pressed;
		}
		else if (mouseButtonUp)
		{
			result = InputEventData.FramePressState.Released;
		}
		else
		{
			result = InputEventData.FramePressState.NotChanged;
		}
		return result;
	}

	private MouseState GetMouseState() {
		InputEventData eventData;
		bool exist = GetPointerData (-1, out eventData, true);
		eventData.Reset ();
		if (exist) {
			eventData.Position = input.mousePosition;
		}
		Vector2 mousePosition = input.mousePosition;
		if (Cursor.lockState == CursorLockMode.Locked)
		{
			eventData.Position = new Vector2(-1f, -1f);
			eventData.Delta = Vector2.zero;
		}
		else
		{
			eventData.Delta = mousePosition - eventData.Position;
			eventData.Position = mousePosition;
		}
		this.eventManager.RaycastAll (eventData, m_RaycastResultCache);
		stRaycastResult currentRaycast = FindFirstRaycast (m_RaycastResultCache);
		eventData.CurrentRaycast = currentRaycast;
		this.m_RaycastResultCache.Clear ();
		this.m_MouseState.SetButtonState (this.StateForMouseButton (), eventData);
		return this.m_MouseState;
	}

	private void ProcessMousePress(ButtonState data) {
		InputEventData eventData = data.buttonData;
		GameObject gameObject = eventData.CurrentRaycast.gameObject;
		if (data.PressedThisFrame ()) {
			eventData.eligibleForClick = true;
			eventData.Delta = Vector2.zero;
			eventData.dragging = false;
			eventData.useDragThreshold = true;
			eventData.pressPosition = eventData.Position;
			eventData.PressRaycast = eventData.CurrentRaycast;
			GameObject gameObject2 = InputExecuteEvents.ExecuteHierarchy<IInputDownHandler>(gameObject, eventData, InputExecuteEvents.InputDownHandler);
			if (gameObject2 == null)
			{
				gameObject2 = InputExecuteEvents.GetEventHandler<IInputClickHandler>(gameObject);
			}
			float unscaledTime = Time.unscaledTime;
			if (gameObject2 == eventData.lastPress)
			{
				float num = unscaledTime - eventData.clickTime;
				if (num < 0.3f)
				{
					eventData.clickCount++;
				}
				else
				{
					eventData.clickCount = 1;
				}
				eventData.clickTime = unscaledTime;
			}
			else
			{
				eventData.clickCount = 1;
			}
			eventData.pointerPress = gameObject2;
			eventData.rawPointerPress = gameObject;
			eventData.clickTime = unscaledTime;
			eventData.pointerDrag = InputExecuteEvents.GetEventHandler<IInputDragHandler>(gameObject);
		}
		if (data.ReleasedThisFrame ()) {
			InputExecuteEvents.Execute<IInputUpHandler>(eventData.pointerPress, eventData, InputExecuteEvents.InputUpHandler);
			GameObject eventHandler = InputExecuteEvents.GetEventHandler<IInputClickHandler>(gameObject);
			if (eventData.pointerPress == eventHandler && eventData.eligibleForClick)
			{
				InputExecuteEvents.Execute<IInputClickHandler>(eventData.pointerPress, eventData, InputExecuteEvents.InputClickHandler);
			}
			eventData.eligibleForClick = false;
			eventData.pointerPress = null;
			eventData.rawPointerPress = null;
			if (eventData.pointerDrag != null && eventData.dragging)
			{
				InputExecuteEvents.Execute<IInputEndDragHandler>(eventData.pointerDrag, eventData, InputExecuteEvents.InputEndDragHandler);
			}
			eventData.dragging = false;
			eventData.pointerDrag = null;
			if (gameObject != eventData.pointerEnter)
			{
				this.HandlePointerExitAndEnter(eventData, null); //exit当前所有hovered的对象
				this.HandlePointerExitAndEnter(eventData, gameObject); //enter当前对象
			}
		}
	}
	#endregion


	#region process
	protected virtual void ProcessMove(InputEventData pointerEvent)
	{
		GameObject newEnterTarget = (Cursor.lockState == CursorLockMode.Locked) ? null : pointerEvent.CurrentRaycast.gameObject;
		this.HandlePointerExitAndEnter(pointerEvent, newEnterTarget);
	}

	protected virtual void ProcessDrag(InputEventData pointerEvent)
	{
		if (pointerEvent.IsPointerMoving() && Cursor.lockState != CursorLockMode.Locked && pointerEvent.pointerDrag != null)
		{
			if (!pointerEvent.dragging && ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.Position, (float)eventManager.pixelDragThreshold, pointerEvent.useDragThreshold))
			{
				InputExecuteEvents.Execute<IInputBeginDragHandler>(pointerEvent.pointerDrag, pointerEvent, InputExecuteEvents.InputBeginDragHandler);
				pointerEvent.dragging = true;
			}
			if (pointerEvent.dragging)
			{
				if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
				{
					InputExecuteEvents.Execute<IInputUpHandler>(pointerEvent.pointerPress, pointerEvent, InputExecuteEvents.InputUpHandler);
					pointerEvent.eligibleForClick = false;
					pointerEvent.pointerPress = null;
					pointerEvent.rawPointerPress = null;
				}
				InputExecuteEvents.Execute<IInputDragHandler>(pointerEvent.pointerDrag, pointerEvent, InputExecuteEvents.InputDragHandler);
			}
		}
	}
	#endregion

	protected static stRaycastResult FindFirstRaycast(List<stRaycastResult> candidates)
	{
		stRaycastResult result;
		for (int i = 0; i < candidates.Count; i++)
		{
			if (candidates[i].gameObject != null)
			{
				result = candidates[i];
				return result;
			}
		}
		result = default(stRaycastResult);
		return result;
	}

	protected void HandlePointerExitAndEnter(InputEventData currentPointerData, GameObject newEnterTarget)
	{
		if (newEnterTarget == null || currentPointerData.pointerEnter == null)
		{
			for (int i = 0; i < currentPointerData.hovered.Count; i++)
			{
				InputExecuteEvents.Execute<IInputExitHandler>(currentPointerData.hovered[i], currentPointerData, InputExecuteEvents.InputExitHandler);
			}
			currentPointerData.hovered.Clear();
			if (newEnterTarget == null)
			{
				currentPointerData.pointerEnter = newEnterTarget;
				return;
			}
		}
		if (currentPointerData.pointerEnter != newEnterTarget || newEnterTarget != null)
		{
			GameObject gameObject = FindCommonRoot(currentPointerData.pointerEnter, newEnterTarget);
			if (currentPointerData.pointerEnter != null)
			{
				Transform transform = currentPointerData.pointerEnter.transform;
				//当前对象往上遍历
				while (transform != null)
				{
					if (gameObject != null && gameObject.transform == transform) //如果有相同父对象，并且==当前对象
					{
						break;
					}
					InputExecuteEvents.Execute<IInputExitHandler>(transform.gameObject, currentPointerData, InputExecuteEvents.InputExitHandler);
					currentPointerData.hovered.Remove(transform.gameObject);
					transform = transform.parent;
				}
			}
			currentPointerData.pointerEnter = newEnterTarget;
			if (newEnterTarget != null)
			{
				Transform transform2 = newEnterTarget.transform;
				while (transform2 != null && transform2.gameObject != gameObject)
				{
					InputExecuteEvents.Execute<IInputEnterHandler>(transform2.gameObject, currentPointerData, InputExecuteEvents.InputEnterHandler);
					currentPointerData.hovered.Add(transform2.gameObject);
					transform2 = transform2.parent;
				}
			}
		}
	}

	/// <summary>
	/// 寻找共同的父对象，没有的话返回null
	/// </summary>
	/// <returns>The common root.</returns>
	/// <param name="g1">G1.</param>
	/// <param name="g2">G2.</param>
	protected static GameObject FindCommonRoot(GameObject g1, GameObject g2)
	{
		GameObject result;
		if (g1 == null || g2 == null)
		{
			result = null;
		}
		else
		{
			Transform transform = g1.transform;
			while (transform != null)
			{
				Transform transform2 = g2.transform;
				while (transform2 != null)
				{
					if (transform == transform2)
					{
						result = transform.gameObject;
						return result;
					}
					transform2 = transform2.parent;
				}
				transform = transform.parent;
			}
			result = null;
		}
		return result;
	}

	private static bool ShouldStartDrag(Vector2 pressPos, Vector2 currentPos, float threshold, bool useDragThreshold)
	{
		return !useDragThreshold || (pressPos - currentPos).sqrMagnitude >= threshold * threshold;
	}

	protected void RemovePointerData(InputEventData data)
	{
		this.m_PointerData.Remove(data.PointerId);
	}

	public bool IsPointerOverGameObject(int pointerId)
	{
		InputEventData lastPointerEventData = this.GetLastPointerEventData(pointerId);
		return lastPointerEventData != null && lastPointerEventData.pointerEnter != null;
	}

	protected InputEventData GetLastPointerEventData(int id)
	{
		InputEventData result;
		this.GetPointerData(id, out result, false);
		return result;
	}
}
